﻿using System;
using System.Collections.Generic;

namespace ZOIL.DataModel
{
    /// <summary>
    /// Attribute that is used to specify that a class is a view for the given type
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    public class ViewFor : Attribute
    {
        /// <summary>
        /// the dictionary that holds all types of views for all types
        /// </summary>
        private static readonly Dictionary<Type, Dictionary<String, Type>> ViewsFor = new Dictionary<Type, Dictionary<string, Type>>();
        /// <summary>
        /// Internal Method to bind a different view
        /// Is used for ICollectionPersistableCollection: a non-db version is defined in Zoil.DataModel Assembly while a DB-Version is implemented in Zoil.Backend.BaseX which should use the same View(Model), but should (and can't) referenced by Zoil.DataModel assembly
        /// </summary>
        public static void AddViewFor(Type t, Dictionary<String, Type> views)
        {
            ViewsFor[t] = views;
        }

        /// <summary>
        /// An index which identifies the view, since there can be multiple different views for every type
        /// </summary>
        public string Key { get; private set; }

        /// <summary>
        /// The TargetType for which the attributed class is a view for
        /// </summary>
        public Type Type { get; private set; }

        public bool IncludeTypeHierarchy { get; private set; }

        public ViewFor(Type type, string key, bool includeTypeHierarchy = false)
        {
            Type = type;
            Key = key;
            IncludeTypeHierarchy = includeTypeHierarchy;
        }

        /// <summary>
        /// initialize the Dictionary
        /// get all types from the plugin manager, that contain the ViewFor attribute
        /// note: you have to initialize the pluginmanager to register types for the attribute
        /// </summary>
        /// <param name="type"></param>
        public static void InitViewsFor(Type type)
        {
            var views = new Dictionary<string, Type>();

            var viewForTypes = PluginManager.Instance.GetAttributedTypes(typeof(ViewFor));

            foreach (var viewForType in viewForTypes)
            {
                var attributes = GetCustomAttributes(viewForType);

                foreach (var attribute in attributes)
                {
                    if (attribute is ViewFor)
                    {
                        var viewFor = attribute as ViewFor;
                        if (viewFor.Type == type)
                        {
                            views[viewFor.Key] = viewForType;
                        }
                        else if (viewFor.IncludeTypeHierarchy && type.IsAssignableFrom(viewFor.Type))
                        {
                            views[viewFor.Key] = viewForType;
                        }
                    }
                }
            }
            ViewsFor[type] = views;
        }

        /// <summary>
        /// returns a dictionary of views for the given type
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Dictionary<string, Type> GetViewsFor(Type type)
        {
            if (!ViewsFor.ContainsKey(type))
                InitViewsFor(type);

            if (ViewsFor.ContainsKey(type))
                return ViewsFor[type];

            return new Dictionary<string, Type>();
        }
    }
}